#include "Application.hpp"

Application::Application(const char *title, int x, int y, int w, int h, Uint32 flags)
{
    this->win = SDL_CreateWindow(title, x, y, w, h, flags);
    this->renderer = SDL_CreateRenderer(win, -1, 0);

    delay = 0;
    real_delta = 0;
    delta = 0;
    current_fps = 0;
    msDelay = 1000 / 60;

    width = w;
    height = height;
}

Application::~Application()
{
    SDL_DestroyRenderer(this->renderer);
    SDL_DestroyWindow(this->win);
    while (!this->scenes.empty())
    {
        delete this->scenes.top();
        this->scenes.pop();
    }
}

int Application::exec()
{
    SDL_Event evt;
    Timer fps_timer;
    fps_timer.recordTimePoint();
    int frame = 0;
    Timer seconds_timer;
    seconds_timer.recordTimePoint();
    current_fps = 0;
    Timer delay_timer;
    long long updateDelta = msDelay;
    while (!this->exited)
    {
        Uint32 flags = SDL_GetWindowFlags(win);
        while (SDL_PollEvent(&evt))
        {
            if (evt.type == SDL_QUIT)
            {
                exited = true;
            }
            else if (evt.type == SDL_KEYDOWN)
            {
                this->keyDown(evt.key);
            }else if (evt.type == SDL_WINDOWEVENT && evt.window.event==SDL_WINDOWEVENT_RESIZED){
                if(!this->scenes.empty()){
                    width = evt.window.data1;
                    height = evt.window.data2;
                    // Log::log("resize %d %d\n",width,height);
                }
                // Log::log("resize\n");
            }
            // Log::log("i:%d",evt.type);
            if (!this->scenes.empty())
            {
                this->scenes.top()->_init();
                int res = this->scenes.top()->handleEvent(evt);
                if (res == -1)
                {
                    delete this->scenes.top();
                    this->scenes.pop();
                }
                else if (res == -2)
                {
                    this->exited = true;
                }
            }
        }
        fps_timer.recordTimePoint();
        if (updateDelta > 0)
        {
            this->update(updateDelta / 1000.f);
            if (!(flags & SDL_WINDOW_MINIMIZED))
            {
                this->render();
            }
            frame++;
        }
        delta = fps_timer.getElapsedMilliseconds();
        delay = updateDelta - delta;
        if (delay > 0)
        {
            SDL_Delay(delay);
            updateDelta = msDelay;
        }
        else
        {
            updateDelta = delta;
        }
        if (seconds_timer.getElapsedSeconds() >= 1)
        {
            current_fps = frame;
            seconds_timer.recordTimePoint();
            frame = 0;
        }
    }
    return 0;
}

void Application::setFps(int fps)
{
    if (fps <= 1)
    {
        fps = 1;
    }
    if (fps >= 1000)
    {
        fps = 1000;
    }
    this->msDelay = 1000 / (float)fps;
}

bool Application::init()
{
    if (Static::init())
    {
        Component::setDefaultFont(Static::default_font);
        return true;
    }
    return false;
}

void Application::quit()
{
    Static::quit();
}

void Application::showFps(bool value)
{
    this->isShowFps = value;
}

void Application::addScene(Scene *scene)
{
    if (scene->width == 0 || scene->height == 0)
    {
        SDL_GetWindowSize(win, &scene->width, &scene->height);
    }
    scenes.push(scene);
}
void Application::getWindowSize(int *w, int *h)
{
    SDL_GetWindowSize(win, w, h);
}
void Application::render()
{
    SDL_SetRenderDrawColor(this->renderer, 0, 0, 0, 0);
    SDL_SetRenderDrawBlendMode(renderer,SDL_BlendMode::SDL_BLENDMODE_NONE);
    SDL_RenderClear(this->renderer);
    SDL_Rect rc{0, 0, 100, 100};
    if (!this->scenes.empty())
    {
        this->scenes.top()->render(this->renderer);
    }
    if (this->isShowFps)
    {
        char buff[0xff];
        SDL_snprintf(buff, 0xff, "fps:%ld", current_fps);
        TTF_SizeUTF8(Static::default_font, buff, &rc.w, &rc.h);
        SDL_Surface *sur_text = TTF_RenderUTF8_Blended(Static::default_font, buff, {0, 0xff, 0, 0xff});
        SDL_Texture *tex_text = SDL_CreateTextureFromSurface(this->renderer, sur_text);
        SDL_RenderCopy(this->renderer, tex_text, nullptr, &rc);
        SDL_DestroyTexture(tex_text);
        SDL_FreeSurface(sur_text);
    }
    if (!this->scenes.empty())
    {
        auto list = this->scenes.top()->getViewableData();
        if (!list.empty())
        {
            SDL_Rect rc_list{0, rc.h, 100, 100};
            for (auto it = list.begin(); it != list.end(); ++it)
            {
                TTF_SizeUTF8(Static::default_font, (*it).c_str(), &rc_list.w, &rc_list.h);
                SDL_Surface *sur_text = TTF_RenderUTF8_Blended(Static::default_font, (*it).c_str(), {0, 0xff, 0, 0xff});
                SDL_Texture *tex_text = SDL_CreateTextureFromSurface(this->renderer, sur_text);
                SDL_RenderCopy(this->renderer, tex_text, nullptr, &rc_list);
                SDL_DestroyTexture(tex_text);
                SDL_FreeSurface(sur_text);
                rc_list.y += rc_list.h;
            }
        }
    }
    SDL_RenderPresent(this->renderer);
}

int Application::update(real delta)
{
    int res = 0;
    if (!this->scenes.empty())
    {
        res = this->scenes.top()->update(delta);
    }
    return res;
}

void Application::keyDown(SDL_KeyboardEvent &evt)
{
    switch (evt.keysym.sym)
    {
    case SDLK_ESCAPE:
        exited = true;
        break;
    default:
        break;
    }
}
